#include "../../includes/NeonMath/Matrix.hpp"
using namespace Neon;

Matrix Matrix::identity = Matrix(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);

Matrix::Matrix(bool isIdentity)
{
	if(isIdentity) {
		qFill(data(), data() + 16, 0.0f);
		mat(0, 1.0f);
		mat(5, 1.0f);
		mat(10, 1.0f);
		mat(15, 1.0f);
	}
}

Matrix::Matrix(const nfloat m11, const nfloat m12, const nfloat m13, const nfloat m14,
			   const nfloat m21, const nfloat m22, const nfloat m23, const nfloat m24,
			   const nfloat m31, const nfloat m32, const nfloat m33, const nfloat m34,
			   const nfloat m41, const nfloat m42, const nfloat m43, const nfloat m44)
{
	mat(0, m11); mat(1, m12); mat(2, m13); mat(3, m14);
	mat(4, m21); mat(5, m22); mat(6, m23); mat(7, m24);
	mat(8, m31); mat(9, m32); mat(10, m33); mat(11, m34);
	mat(12, m41); mat(13, m42); mat(14, m43); mat(15, m44);
}

Matrix::Matrix(const Matrix &mat)
{
	qCopy(mat.data(), mat.data() + 16, data());
}

Matrix::Matrix(const nfloat mat)
{
	qFill(data(), data() + 16, mat);
}

Matrix::Matrix(const nfloat *mat)
{
	qCopy(mat, mat + 16, data());
}

Matrix&
Matrix::operator =(const Matrix &rhs)
{
	qCopy(rhs.data(), rhs.data() + 16, data());

	return *this;
}

Matrix&
Matrix::operator =(const nfloat rhs)
{
	qFill(data(), data(), rhs);

	return *this;
}

Matrix&
Matrix::operator =(const nfloat *rhs)
{
	qCopy(rhs, rhs + 16, data());

	return *this;
}

Matrix
Matrix::operator +() const
{
	Matrix tmp(false);

	for(nuint8 i = 0; i < 16; ++i) {
		tmp.mat(i, +mat(i));
	}

	return tmp;
}

Matrix
Matrix::operator +(const Matrix &rhs) const
{
	Matrix tmp(false);

	for(nuint8 i = 0; i < 16; ++i) {
		tmp.mat(i, mat(i) + rhs.mat(i));
	}

	return tmp;
}

Matrix
Matrix::operator +(nfloat rhs) const
{
	Matrix tmp(false);

	for(nuint8 i = 0; i < 16; ++i) {
		tmp.mat(i, mat(i) + rhs);
	}

	return tmp;
}

Matrix
Matrix::operator +(const nfloat *rhs) const
{
	Matrix tmp(false);

	for(nuint8 i = 0; i < 16; ++i) {
		tmp.mat(i, mat(i) + rhs[i]);
	}

	return tmp;
}

Matrix&
Matrix::operator +=(const Matrix &rhs)
{
	(*this) = (*this) + rhs;

	return *this;
}

Matrix&
Matrix::operator +=(nfloat rhs)
{
	(*this) = (*this) + rhs;

	return *this;
}

Matrix&
Matrix::operator +=(const nfloat *rhs)
{
	(*this) = (*this) + rhs;

	return *this;
}

Matrix
Matrix::operator -() const
{
	Matrix tmp(false);

	for(nuint8 i = 0; i < 16; ++i) {
		tmp.mat(i, -mat(i));
	}

	return tmp;
}

Matrix
Matrix::operator -(const Matrix &rhs) const
{
	Matrix tmp(false);

	for(nuint8 i = 0; i < 16; ++i) {
		tmp.mat(i, mat(i) - rhs.mat(i));
	}

	return tmp;
}

Matrix
Matrix::operator -(nfloat rhs) const
{
	Matrix tmp(false);

	for(nuint8 i = 0; i < 16; ++i) {
		tmp.mat(i, mat(i) - rhs);
	}

	return tmp;
}

Matrix
Matrix::operator -(const nfloat *rhs) const
{
	Matrix tmp(false);

	for(nuint8 i = 0; i < 16; ++i) {
		tmp.mat(i, mat(i) - rhs[i]);
	}

	return tmp;
}

Matrix&
Matrix::operator -=(const Matrix &rhs)
{
	(*this) = (*this) - rhs;

	return *this;
}

Matrix&
Matrix::operator -=(nfloat rhs)
{
	(*this) = (*this) - rhs;

	return *this;
}

Matrix&
Matrix::operator -=(const nfloat *rhs)
{
	(*this) = (*this) - rhs;

	return *this;
}

Matrix
Matrix::operator *(const Matrix &rhs) const
{
	return Matrix(
		(mat(0) * rhs.mat(0)) + (mat(1) * rhs.mat(4)) + (mat(2) * rhs.mat(8)) + (mat(3) * rhs.mat(12)),
		(mat(0) * rhs.mat(1)) + (mat(1) * rhs.mat(5)) + (mat(2) * rhs.mat(9)) + (mat(3) * rhs.mat(13)),
		(mat(0) * rhs.mat(2)) + (mat(1) * rhs.mat(6)) + (mat(2) * rhs.mat(10)) + (mat(3) * rhs.mat(14)),
		(mat(0) * rhs.mat(3)) + (mat(1) * rhs.mat(7)) + (mat(2) * rhs.mat(11)) + (mat(3) * rhs.mat(15)),
		(mat(4) * rhs.mat(0)) + (mat(5) * rhs.mat(4)) + (mat(6) * rhs.mat(8)) + (mat(7) * rhs.mat(12)),
		(mat(4) * rhs.mat(1)) + (mat(5) * rhs.mat(5)) + (mat(6) * rhs.mat(9)) + (mat(7) * rhs.mat(13)),
		(mat(4) * rhs.mat(2)) + (mat(5) * rhs.mat(6)) + (mat(6) * rhs.mat(10)) + (mat(7) * rhs.mat(14)),
		(mat(4) * rhs.mat(3)) + (mat(5) * rhs.mat(7)) + (mat(6) * rhs.mat(11)) + (mat(7) * rhs.mat(15)),
		(mat(8) * rhs.mat(0)) + (mat(9) * rhs.mat(4)) + (mat(10) * rhs.mat(8)) + (mat(11) * rhs.mat(12)),
		(mat(8) * rhs.mat(1)) + (mat(9) * rhs.mat(5)) + (mat(10) * rhs.mat(9)) + (mat(11) * rhs.mat(13)),
		(mat(8) * rhs.mat(2)) + (mat(9) * rhs.mat(6)) + (mat(10) * rhs.mat(10)) + (mat(11) * rhs.mat(14)),
		(mat(8) * rhs.mat(3)) + (mat(9) * rhs.mat(7)) + (mat(10) * rhs.mat(11)) + (mat(11) * rhs.mat(15)),
		(mat(12) * rhs.mat(0)) + (mat(13) * rhs.mat(4)) + (mat(14) * rhs.mat(8)) + (mat(15) * rhs.mat(12)),
		(mat(12) * rhs.mat(1)) + (mat(13) * rhs.mat(5)) + (mat(14) * rhs.mat(9)) + (mat(15) * rhs.mat(13)),
		(mat(12) * rhs.mat(2)) + (mat(13) * rhs.mat(6)) + (mat(14) * rhs.mat(10)) + (mat(15) * rhs.mat(14)),
		(mat(12) * rhs.mat(3)) + (mat(13) * rhs.mat(7)) + (mat(14) * rhs.mat(11)) + (mat(15) * rhs.mat(15))
	);
}

Matrix
Matrix::operator *(nfloat rhs) const
{
	Matrix tmp(false);

	for(nuint8 i = 0; i < 16; ++i) {
		tmp.mat(i, mat(i) * rhs);
	}

	return tmp;
}

Matrix
Matrix::operator *(const nfloat *rhs) const
{
	return Matrix(
		(mat(0) * rhs[0]) + (mat(1) * rhs[4]) + (mat(2) * rhs[8]) + (mat(3) * rhs[12]),
		(mat(0) * rhs[1]) + (mat(1) * rhs[5]) + (mat(2) * rhs[9]) + (mat(3) * rhs[13]),
		(mat(0) * rhs[2]) + (mat(1) * rhs[6]) + (mat(2) * rhs[10]) + (mat(3) * rhs[14]),
		(mat(0) * rhs[3]) + (mat(1) * rhs[7]) + (mat(2) * rhs[11]) + (mat(3) * rhs[15]),
		(mat(4) * rhs[0]) + (mat(5) * rhs[4]) + (mat(6) * rhs[8]) + (mat(7) * rhs[12]),
		(mat(4) * rhs[1]) + (mat(5) * rhs[5]) + (mat(6) * rhs[9]) + (mat(7) * rhs[13]),
		(mat(4) * rhs[2]) + (mat(5) * rhs[6]) + (mat(6) * rhs[10]) + (mat(7) * rhs[14]),
		(mat(4) * rhs[3]) + (mat(5) * rhs[7]) + (mat(6) * rhs[11]) + (mat(7) * rhs[15]),
		(mat(8) * rhs[0]) + (mat(9) * rhs[4]) + (mat(10) * rhs[8]) + (mat(11) * rhs[12]),
		(mat(8) * rhs[1]) + (mat(9) * rhs[5]) + (mat(10) * rhs[9]) + (mat(11) * rhs[13]),
		(mat(8) * rhs[2]) + (mat(9) * rhs[6]) + (mat(10) * rhs[10]) + (mat(11) * rhs[14]),
		(mat(8) * rhs[3]) + (mat(9) * rhs[7]) + (mat(10) * rhs[11]) + (mat(11) * rhs[15]),
		(mat(12) * rhs[0]) + (mat(13) * rhs[4]) + (mat(14) * rhs[8]) + (mat(15) * rhs[12]),
		(mat(12) * rhs[1]) + (mat(13) * rhs[5]) + (mat(14) * rhs[9]) + (mat(15) * rhs[13]),
		(mat(12) * rhs[2]) + (mat(13) * rhs[6]) + (mat(14) * rhs[10]) + (mat(15) * rhs[14]),
		(mat(12) * rhs[3]) + (mat(13) * rhs[7]) + (mat(14) * rhs[11]) + (mat(15) * rhs[15])
	);
}

Matrix&
Matrix::operator *=(const Matrix &rhs)
{
	(*this) = (*this) * rhs;

	return *this;
}

Matrix&
Matrix::operator *=(nfloat rhs)
{
	(*this) = (*this) * rhs;

	return *this;
}

Matrix&
Matrix::operator *=(const nfloat *rhs)
{
	(*this) = (*this) * rhs;

	return *this;
}

Matrix
Matrix::createLookAt(const Vector3 &camera, const Vector3 &centre, const Vector3 &up)
{
	Matrix tmp;

	Vector3 zAxis(Vector3::normalise(camera - centre));
	Vector3 xAxis(Vector3::normalise(Vector3::crossProduct(up, zAxis)));
	Vector3 yAxis(Vector3::crossProduct(zAxis, xAxis));

	tmp.mat(0, xAxis.X);
	tmp.mat(1, yAxis.X);
	tmp.mat(2, zAxis.X);

	tmp.mat(4, xAxis.Y);
	tmp.mat(5, yAxis.Y);
	tmp.mat(6, zAxis.Y);

	tmp.mat(8, xAxis.Z);
	tmp.mat(9, yAxis.Z);
	tmp.mat(10, zAxis.Z);

	tmp.mat(12, -Vector3::dotProduct(xAxis, camera));
	tmp.mat(13, -Vector3::dotProduct(yAxis, camera));
	tmp.mat(14, -Vector3::dotProduct(zAxis, camera));

	return tmp;
}

Matrix
Matrix::createOrthographic(const nfloat width, const nfloat height, const nfloat zNearPlane, const nfloat zFarPlane)
{
	Matrix tmp;

	tmp.mat(0, 2.0f / width);
	tmp.mat(5, 2.0f / height);
	tmp.mat(10, 1.0f / (zNearPlane - zFarPlane));
	tmp.mat(14, zNearPlane / (zNearPlane - zFarPlane));

	return tmp;
}

Matrix
Matrix::createOrthoGraphicOffCentre(const Vector3 &leftRight, const Vector3 &bottomTop, const nfloat zNearPlane, const nfloat zFarPlane)
{
	Matrix tmp;

	nfloat left = leftRight.X;
	nfloat right = leftRight.Y;
	nfloat bottom = bottomTop.X;
	nfloat top = bottomTop.Y;

	tmp.mat(0, 2.0f / (right - left));
	tmp.mat(5, 2.0f / (top - bottom));
	tmp.mat(10, 1.0f / (zNearPlane - zFarPlane));
	tmp.mat(12, (left + right) / (left - right));
	tmp.mat(13, (top + bottom) / (top - bottom));
	tmp.mat(14, zNearPlane / (zNearPlane - zFarPlane));

	return tmp;
}

Matrix
Matrix::createPerspective(const nfloat width, const nfloat height, const nfloat nearPlaneDistance, const nfloat farPlaneDistance)
{
	Q_ASSERT_X(nearPlaneDistance >= 0.0f, "Matrix::createPerspective", "Near plane lower than or equal to 0.0f");
	Q_ASSERT_X(farPlaneDistance > 0.0f, "Matrix::createPerspective", "Far plane lower than 0.0f");
	Q_ASSERT_X(farPlaneDistance >= nearPlaneDistance, "Matrix::createPerspective", "Far plane must be greater than the Near plane");

	Matrix tmp;

	tmp.mat(0, (2.0f * nearPlaneDistance) / width);
	tmp.mat(5, (2.0f * nearPlaneDistance) / height);
	tmp.mat(10, farPlaneDistance / (nearPlaneDistance - farPlaneDistance));
	tmp.mat(11, -1.0f);
	tmp.mat(14, (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance));
	tmp.mat(15, 0.0f);

	return tmp;
}

Matrix
Matrix::createPerspectiveFieldOfView(const nfloat fieldOfView, const nfloat aspectRatio, const nfloat nearPlaneDistance, const nfloat farPlaneDistance)
{
	Q_ASSERT_X((fieldOfView > 0.0f) && (fieldOfView <= 3.141593f), "Matrix::createPerspectiveFieldOfView", "fieldOfView must be greater than 0.0f and less than or equal to PI");
	Q_ASSERT_X(nearPlaneDistance >= 0.0f, "Matrix::createPerspectiveFieldOfView", "Near plane must be greater than or equal to 0.0f");
	Q_ASSERT_X(farPlaneDistance > 0.0f, "Matrix::createPerspectiveFieldOfView", "Far plane must be greater than 0.0f");
	Q_ASSERT_X(farPlaneDistance >= nearPlaneDistance, "Matrix::createPerspectiveFieldOfView", " Far plane must be greater than the Near plane");

	Matrix tmp;

	nfloat cotan = 1.0f / tan(fieldOfView * 0.5f);

	tmp.mat(0, cotan / aspectRatio);
	tmp.mat(5, cotan);
	tmp.mat(10, farPlaneDistance / (nearPlaneDistance - farPlaneDistance));
	tmp.mat(11, -1.0f);
	tmp.mat(14, (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance));
	tmp.mat(15, 0.0f);

	return tmp;
}

Matrix
Matrix::createRotationX(nfloat angle)
{
	angle = Math::toRadians(angle);

	Matrix tmp;

	nfloat cos = std::cos(angle);
	nfloat sin = std::sin(angle);

	tmp.mat(5, cos);
	tmp.mat(6, sin);
	tmp.mat(9, -sin);
	tmp.mat(10, cos);

	return tmp;
}

Matrix
Matrix::createRotationY(nfloat angle)
{
	angle = Math::toRadians(angle);

	Matrix tmp;

	nfloat cos = std::cos(angle);
	nfloat sin = std::sin(angle);

	tmp.mat(0, cos);
	tmp.mat(2, -sin);
	tmp.mat(8, sin);
	tmp.mat(10, cos);

	return tmp;
}

Matrix
Matrix::createRotationZ(nfloat angle)
{
	angle = Math::toRadians(angle);

	Matrix tmp;

	nfloat cos = std::cos(angle);
	nfloat sin = std::sin(angle);

	tmp.mat(0, cos);
	tmp.mat(1, sin);
	tmp.mat(4, -sin);
	tmp.mat(5, cos);

	return tmp;
}

Matrix
Matrix::createScale(const Vector3 &scales)
{
	return createScale(scales.X, scales.Y, scales.Z);
}

Matrix
Matrix::createScale(const nfloat scale)
{
	return createScale(scale, scale, scale);
}

Matrix
Matrix::createScale(const nfloat x, const nfloat y, const nfloat z)
{
	Matrix tmp;

	tmp.mat(0, x);
	tmp.mat(5, y);
	tmp.mat(10, z);

	return tmp;
}

Matrix
Matrix::createTranslation(const Vector3 &position)
{
	return createTranslation(position.X, position.Y, position.Z);
}

Matrix
Matrix::createTranslation(const nfloat x, const nfloat y, const nfloat z)
{
	Matrix tmp;

	tmp.mat(12, x);
	tmp.mat(13, y);
	tmp.mat(14, z);

	return tmp;
}

nfloat
Matrix::determinant() const
{
	nfloat a0 = mat(0) * mat(5) - mat(1) * mat(4);
	nfloat a1 = mat(0) * mat(6) - mat(2) * mat(4);
	nfloat a2 = mat(0) * mat(7) - mat(3) * mat(4);
	nfloat a3 = mat(1) * mat(6) - mat(2) * mat(5);
	nfloat a4 = mat(1) * mat(7) - mat(3) * mat(5);
	nfloat a5 = mat(2) * mat(7) - mat(3) * mat(6);

	nfloat b0 = mat(8)  * mat(13) - mat(9)  * mat(12);
	nfloat b1 = mat(8)  * mat(14) - mat(10) * mat(12);
	nfloat b2 = mat(8)  * mat(15) - mat(11) * mat(12);
	nfloat b3 = mat(9)  * mat(14) - mat(10) * mat(13);
	nfloat b4 = mat(9)  * mat(15) - mat(11) * mat(13);
	nfloat b5 = mat(10) * mat(15) - mat(11) * mat(14);

	return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
}

void
Matrix::fromAxisAngle(const Vector3 &axis, nfloat angle)
{
	angle = Math::toRadians(angle);

	nfloat sin = std::sin(angle);
	nfloat cos = std::cos(angle);

	nfloat xx = axis.X * axis.X;
	nfloat yy = axis.Y * axis.Y;
	nfloat zz = axis.Z * axis.Z;
	nfloat xy = axis.X * axis.Y;
	nfloat xz = axis.X * axis.Z;
	nfloat yz = axis.Y * axis.Z;

	mat(0, xx + (cos * (1.0f - xx)));
	mat(1, (xy - (cos * xy)) + (sin * axis.Z));
	mat(2, (xz - (cos * xz)) - (sin * axis.Y));

	mat(4, (xy - (cos * xy)) - (sin * axis.Z));
	mat(5, yy + (cos * (1.0f - yy)));
	mat(6, (yz - (cos * yz)) + (sin * axis.X));

	mat(8, (xz - (cos * xz)) + (sin * axis.Y));
	mat(9, (yz - (cos * yz)) - (sin * axis.X));
	mat(10, zz + (cos * 1.0f - zz));
}

Matrix
Matrix::invert(const Matrix &matrix)
{
	nfloat a0 = matrix.mat(0) * matrix.mat(5) - matrix.mat(1) * matrix.mat(4);
	nfloat a1 = matrix.mat(0) * matrix.mat(6) - matrix.mat(2) * matrix.mat(4);
	nfloat a2 = matrix.mat(0) * matrix.mat(7) - matrix.mat(3) * matrix.mat(4);
	nfloat a3 = matrix.mat(1) * matrix.mat(6) - matrix.mat(2) * matrix.mat(5);
	nfloat a4 = matrix.mat(1) * matrix.mat(7) - matrix.mat(3) * matrix.mat(5);
	nfloat a5 = matrix.mat(2) * matrix.mat(7) - matrix.mat(3) * matrix.mat(6);

	nfloat b0 = matrix.mat(8)  * matrix.mat(13) - matrix.mat(9)  * matrix.mat(12);
	nfloat b1 = matrix.mat(8)  * matrix.mat(14) - matrix.mat(10) * matrix.mat(12);
	nfloat b2 = matrix.mat(8)  * matrix.mat(15) - matrix.mat(11) * matrix.mat(12);
	nfloat b3 = matrix.mat(9)  * matrix.mat(14) - matrix.mat(10) * matrix.mat(13);
	nfloat b4 = matrix.mat(9)  * matrix.mat(15) - matrix.mat(11) * matrix.mat(13);
	nfloat b5 = matrix.mat(10) * matrix.mat(15) - matrix.mat(11) * matrix.mat(14);

	nfloat det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;

	Q_ASSERT_X(det != 0, "Matrix::invert", "Determinent is 0. Matrix contains no inverse.");

	Matrix inv(false);

	inv.mat(0,  +matrix.mat(5)  * b5 - matrix.mat(6)  * b4 + matrix.mat(7)  * b3);
	inv.mat(1,  -matrix.mat(1)  * b5 + matrix.mat(2)  * b4 - matrix.mat(3)  * b3);
	inv.mat(2,  +matrix.mat(13) * a5 - matrix.mat(14) * a4 + matrix.mat(15) * a3);
	inv.mat(3,  -matrix.mat(9)  * a5 + matrix.mat(10) * a4 - matrix.mat(11) * a3);
	inv.mat(4,  -matrix.mat(4)  * b5 + matrix.mat(6)  * b2 - matrix.mat(7)  * b1);
	inv.mat(5,  +matrix.mat(0)  * b5 - matrix.mat(2)  * b2 + matrix.mat(3)  * b1);
	inv.mat(6,  -matrix.mat(12) * a5 + matrix.mat(14) * a2 - matrix.mat(15) * a1);
	inv.mat(7,  +matrix.mat(8)  * a5 - matrix.mat(10) * a2 + matrix.mat(11) * a1);
	inv.mat(8,  +matrix.mat(4)  * b4 - matrix.mat(5)  * b2 + matrix.mat(7)  * b0);
	inv.mat(9,  -matrix.mat(0)  * b4 + matrix.mat(1)  * b2 - matrix.mat(3)  * b0);
	inv.mat(10, +matrix.mat(12) * a4 - matrix.mat(13) * a2 + matrix.mat(15) * a0);
	inv.mat(11, -matrix.mat(8)  * a4 + matrix.mat(9)  * a2 - matrix.mat(11) * a0);
	inv.mat(12, -matrix.mat(4)  * b3 + matrix.mat(5)  * b1 - matrix.mat(6)  * b0);
	inv.mat(13, +matrix.mat(0)  * b3 - matrix.mat(1)  * b1 + matrix.mat(2)  * b0);
	inv.mat(14, -matrix.mat(12) * a3 + matrix.mat(13) * a1 - matrix.mat(14) * a0);
	inv.mat(15, +matrix.mat(8)  * a3 - matrix.mat(9)  * a1 + matrix.mat(10) * a0);

	det = 1.0f / det;
	for(nuint8 i = 0; i < 16; ++i) {
		inv.mat(i, inv.mat(i) * det);
	}

	return inv;
}

Matrix
Matrix::lerp(const Matrix &mat1, const Matrix &mat2, const nfloat amount)
{
	return mat1 + ((mat2 - mat1) * amount);
}

void
Matrix::mat(const nuint8 index, const nfloat value)
{
	Q_ASSERT_X(index >= 0 && index <= 15, "Matrix::mat", "Index out of range.");
	m[index] = value;
}

nfloat
Matrix::mat(const nuint8 index) const
{
	Q_ASSERT_X(index >= 0 && index <= 15, "Matrix::mat", "Index out of range.");
	return m[index];
}
Matrix
Matrix::transpose(const Matrix &matrix)
{
	return Matrix(
		matrix.mat(0), matrix.mat(4), matrix.mat(8), matrix.mat(12),
		matrix.mat(1), matrix.mat(5), matrix.mat(9), matrix.mat(13),
		matrix.mat(2), matrix.mat(6), matrix.mat(10), matrix.mat(14),
		matrix.mat(3), matrix.mat(7), matrix.mat(11), matrix.mat(15)
	);
}

Matrix&
Matrix::transpose()
{
	qSwap(m[1], m[4]);
	qSwap(m[8], m[2]);
	qSwap(m[12], m[3]);
	qSwap(m[9], m[6]);
	qSwap(m[13], m[7]);
	qSwap(m[14], m[11]);

	return *this;
}